package com.quotes.simulation;

import java.util.*;
import javax.swing.event.EventListenerList;
import org.apache.log4j.Logger;
import com.quotes.Parameters;
import com.quotes.datamodell.*;
import com.quotes.indicators.AdxIndicator;

public class Portfolio 
{
	private Logger logger = Logger.getLogger(getClass().toString());
	private QuotesList quotesList = null;
	private Parameters parameters = null;
	private EventListenerList listenerList = new EventListenerList();
	private PositionList positionList = new PositionList();
	private double cash;
	//private TreeMap<Symbol, TimeSeries> atr = new TreeMap<Symbol, TimeSeries>();
	private Date tradingDate;
	private TimeSeries valueDevelopment = new TimeSeries();
	private int buyCount = 0;
	private int sellCount = 0;
	private double wins = 0;
	private double losses = 0;
	private double fees = 0;
	private int lowOnCashCount = 0;

	public Portfolio(QuotesList quotesList, Parameters parameters) {
		this.quotesList = quotesList;
		this.parameters = parameters;
		this.cash = parameters.getAsDouble("initialCash");
		
//		for (Quotes quotes : quotesList.getQuotes()) {
//			atr.put(quotes.getSymbol(), AdxIndicator.calculateAverageTrueRange(quotes, parameters.getAsInt("atr.ticks")));
//		}
	}
	
	public void setTradingDate(Date tradingDate) {
		if (this.tradingDate != null) {
			valueDevelopment.put(this.tradingDate, this.getCurrentValue());
		}

		this.sellOnStopLoss();
		this.tradingDate = tradingDate;
	}
	
	public void addPortfolioChangedEventListener(PortfolioChangedEventListener listener) {
        listenerList.add(PortfolioChangedEventListener.class, listener);
    }
    
    public void removePortfolioChangedEventListener(PortfolioChangedEventListener listener) {
        listenerList.remove(PortfolioChangedEventListener.class, listener);
    }
    
    private void firePortfolioChangedEvent(PortfolioChangedEvent event) {
        Object[] listeners = listenerList.getListenerList();

        for (int i=0; i<listeners.length; i+=2) {
            if (listeners[i] == PortfolioChangedEventListener.class) {
                ((PortfolioChangedEventListener)listeners[i+1]).positionChanged(event);
            }
        }
    }
    
	public void buy(MarketOrder buyOrder) {
		buyOrder.setDate(this.tradingDate);
		buyOrder.setPrice(quotesList.BySymbol(buyOrder.getSymbol()).getQuote(QuotesType.CLOSE).get(this.tradingDate));
		this.setBuyCountAndStopLossOnOrder(buyOrder);
	
		double value = buyOrder.getValue();
		double buyFee = parameters.getAsDouble("fee.buy");
				
		if (cash < value + buyFee) {
			lowOnCashCount++;
			return;
		}
		
		Position position = new Position(buyOrder);
		positionList.add(position);
		cash = cash - value - buyFee;
		buyCount++;
		fees += buyFee;

		PortfolioChangedEvent event = new PortfolioChangedEvent();
		event.buyEvent(buyOrder, cash);
		firePortfolioChangedEvent(event);
	}
	
	public void sell(Position position, MarketOrder sellOrder) {
		if (sellOrder.getPrice() == 0) {
			sellOrder.setPrice(quotesList.BySymbol(sellOrder.getSymbol()).getQuote(QuotesType.CLOSE).get(this.tradingDate));
		}
		
		MarketOrder buyOrder = position.getBuyOrder();
		sellOrder.setCount(buyOrder.getCount());
		
		double buyValue = buyOrder.getValue();
		double sellValue = sellOrder.getValue();
		double sellFee = parameters.getAsDouble("fee.sell");
		
		cash = cash + sellValue - sellFee;
		position.setToBeSold(true);
		
		sellCount++;
		fees += sellFee;
		
		if (sellValue > buyValue)
			wins += sellValue - buyValue;
		else
			losses += buyValue - sellValue;

		PortfolioChangedEvent event = new PortfolioChangedEvent();
		event.sellEvent(position.getBuyOrder(), sellOrder, cash);
		firePortfolioChangedEvent(event);
	}
	
	public void sell(MarketOrder sellOrder) {
		PositionList list = positionList.bySymbol(sellOrder.getSymbol());
		
		for (Position position : list.getPositions()) {
			sell(position, sellOrder);
		}
		
		positionList.removeAllMarkedAsToBeSold();
	}
	
	private void setBuyCountAndStopLossOnOrder(MarketOrder buyOrder) {
		double stopLossPrice = buyOrder.getPrice();
		
//		if (parameters.getAsBoolean("considerAtrForStopLossPrice")) {
//			double atrValue = atr.get(buyOrder.getSymbol()).getOr0(buyOrder.getDate());
//			stopLossPrice = stopLossPrice - atrValue;
//		}

		stopLossPrice = stopLossPrice * parameters.getAsDouble("stopLossPriceFactor");
		buyOrder.setStopLossPrice(stopLossPrice);
		
		double riskValue = parameters.getAsDouble("initialCash") * parameters.getAsDouble("singlePositionAllowedRisk");
		double singlePositionRiskValue = buyOrder.getPrice() - stopLossPrice;
		int count = (int)(riskValue / singlePositionRiskValue);
		buyOrder.setCount(count);
	}

	private void sellOnStopLoss() {
		for (Position position : positionList.getPositions()) {
			MarketOrder buyOrder = position.getBuyOrder();
			double openPrice = quotesList.BySymbol(buyOrder.getSymbol()).getQuote(QuotesType.OPEN).getOr0(tradingDate);
			double closePrice = quotesList.BySymbol(buyOrder.getSymbol()).getQuote(QuotesType.CLOSE).getOr0(tradingDate);
			double lowPrice = quotesList.BySymbol(buyOrder.getSymbol()).getQuote(QuotesType.LOW).getOr0(tradingDate);
			double stopLossPrice = buyOrder.getStopLossPrice();
			
			if (openPrice <= stopLossPrice || closePrice <= stopLossPrice || lowPrice <= stopLossPrice) {
				MarketOrder sellOrder = new MarketOrder(buyOrder.getSymbol(), tradingDate, BuySell.SELL, stopLossPrice);
				sell(position, sellOrder);
			}
		}
		
		positionList.removeAllMarkedAsToBeSold();
	}
	
	public double getCurrentValue() {
		double positionSum = 0;
		
		for (Position position : positionList.getPositions()) {
			Symbol symbol = position.getBuyOrder().getSymbol();
			double closePrice = quotesList.BySymbol(symbol).getQuote(QuotesType.CLOSE).getOr0(tradingDate);
			positionSum += position.getBuyOrder().getCount() * closePrice;
		}
		
		return positionSum + cash;
	}
	
	public SimulationResult getResult() {
		return new SimulationResult(
				parameters.getAsDouble("initialCash"), 
				buyCount, sellCount, lowOnCashCount, wins, losses, fees, valueDevelopment);
	}
}
